/** @file    DeviceX10.cpp
 *  @author  Alessandro Polo
 *  @version $Id: DeviceX10.cpp 2634 2010-06-09 23:01:02Z alex $
 *  @brief
 * File containing methods for the wosh::DeviceX10 class.
 * The header for this class can be found in DeviceX10.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include "DeviceX10.h"

 #include <core/Utilities.h>
 #include <stdlib.h>
 #include <time.h>


using namespace std;
using namespace wosh;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

DeviceX10::DeviceX10( const codeX10& code ) : Object() {
	Object::getURI().setKernelLocal();
	Object::getURI().setPath( "Devices" ); // explicit

	Interfaces.add( wosh::Device::className() );
	Interfaces.add( DeviceX10::className() );

	clear();
	setX10Code(code);
	setName( this->x10codeStr ); //default
 }


DeviceX10::DeviceX10( const DeviceX10& m ) : Object(m) {
	Interfaces = m.Interfaces;

	clear();
	*this = m;
 }

DeviceX10::~DeviceX10() {
	clear();
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////// CONFIGURATION

void DeviceX10::clear() {
	this->x10codeStr = "";

	this->x10code.housecode = ' ';
	this->x10code.unitcode = 0;

	this->type = DeviceX10::UNKNOWN;
	this->alias = "";

	this->state = -1;
 }

void DeviceX10::setX10Code( const codeX10& x10code ) {
	this->x10code = x10code;
	//  BUG

	this->x10codeStr = codeX102string(x10code);

 }

void DeviceX10::setHouseCode( char housecode ) {
	this->x10code.housecode = housecode;
	//  BUG
 }

void DeviceX10::setUnitCode( unsigned short unitcode ) {
	this->x10code.unitcode = unitcode;
	//  BUG
 }

void DeviceX10::setX10Code( const std::string& x10codeS ) {
	this->x10codeStr = x10codeS;
	//  BUG
 }


//////////////////////////////////////////////////////////////////////////////////////////////////////////////

DeviceX10& DeviceX10::operator=(const DeviceX10& m) {
	if (this == &m) return *this; // same object?

	return *this;
 }
  
bool DeviceX10::operator==(const DeviceX10 &other) const {
	if ( this->x10codeStr != other.x10codeStr )
		return false;

	return true;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool DeviceX10::isSwitch() const {
	if ( this->type == DeviceX10::UNKNOWN ) return false;
	return !isSensor();
	///@bug buggy but actually ok
 }

bool DeviceX10::isDimmer() const {
	if ( this->type == DeviceX10::UNKNOWN ) return false;
	if ( !isSwitch() ) return false;

	switch(this->type) {
		case DeviceX10::LM12:		return true;
		case DeviceX10::LM15:		return true;
		case DeviceX10::LM12W:		return true;
		case DeviceX10::LM12I:		return true;
		case DeviceX10::LD11:		return true;
		default:					return false;
	 }
 }

bool DeviceX10::isSensor() const {
	switch(this->type) {
		case DeviceX10::MS13:		return true;
		case DeviceX10::MS13E:		return true;
		default:					return false;
	 }
 }

/*
std::string DeviceX10::getTypeAsString() const
 {
	if ( DeviceX10::isDimmer() )
		return _Dimmer_INTERFACE;
	if ( DeviceX10::isSwitch() )
		return _Switch_INTERFACE;
	if ( DeviceX10::isSensor() )
		return _Device_Type_Sensor;
	return _Device_Type_Unknown;
 }
*/

void DeviceX10::getMethods( wosh::List& methods ) const
 {
	///@todo implement better here
	if ( isSwitch() ) {
		methods.push_back<Variant>( _Switch_METHOD_on );
		methods.push_back<Variant>( _Switch_METHOD_off );
	 }
	if ( isDimmer() ) {
		methods.push_back<Variant>( _Dimmer_METHOD_set );
		methods.push_back<Variant>( _Dimmer_METHOD_dim );
		methods.push_back<Variant>( _Dimmer_METHOD_bright );
	 }
	if ( isSensor() ) {
	 }
 }


/////////////////////////////////////////////////////////////////////////////////////////////// SERIALIZATION

std::string DeviceX10::toString() const
 {
	return "";
 }

/////////////////////////////////////////////////////////////////////////////////////////////// SERIALIZATION
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::vector<std::string> DeviceX10::getKeys() const
 {
	std::vector<std::string> keys;
	keys.push_back( _KEY_name );
	keys.push_back( _KEY_type );

	keys.push_back( _Device_KEY_DeviceStatus );

	keys.push_back( _DeviceX10_KEY_HouseCode );
	keys.push_back( _DeviceX10_KEY_UnitCode );
	keys.push_back( _DeviceX10_KEY_X10Code );
	keys.push_back( _DeviceX10_KEY_X10Module );

	return keys;
 }

Variant DeviceX10::getProperty( const std::string& key ) const
 {
	if ( key == _KEY_name )						return Variant( this->getName() );
	else if ( key == _Device_KEY_DeviceStatus )	return Variant( this->state );
	else if ( key == _DeviceX10_KEY_HouseCode )	return Variant( (int)this->x10code.housecode );
	else if ( key == _DeviceX10_KEY_UnitCode )	return Variant( (int)this->x10code.unitcode );
	else if ( key == _DeviceX10_KEY_X10Code )	return Variant( this->x10codeStr );
	else if ( key == _DeviceX10_KEY_X10Module )	return Variant( DeviceX10::getX10ModuleNameAsString() );
	return Variant::EmptyVariant;
 }

WRESULT DeviceX10::setProperty( const std::string& key, const wosh::Variant& value )
 {
	if ( key == _KEY_name ) {
		return WRET_ERR_ILLEGAL_USE;
	 }
	else if ( key == _DeviceX10_KEY_HouseCode ) {
		std::string charstr = value.toString();
		if ( charstr.size() > 0 )
			setHouseCode( charstr.at(0) );
	 }
	else if ( key == _DeviceX10_KEY_UnitCode ) {
		int code = value.toInteger(-1);
		setUnitCode(code);
		return WRET_OK;
	 }
	else if ( key == _DeviceX10_KEY_X10Code ) {
		setX10Code( value.toString() );
		return WRET_OK;
	 }
	return WRET_ERR_UNKNOWN;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

DataFieldCollector<DeviceX10>* DeviceX10::getDataFields() {
	DataFieldCollector<DeviceX10>* fields = new DataFieldCollector<DeviceX10>();
	fields->add<const std::string&, Variant>(&DeviceX10::getName, "Name", 0);
	fields->add<const std::string&, Variant>(&DeviceX10::getAlias, "Alias", 1);
	fields->add<const std::string&, Variant>(&DeviceX10::getX10Code, "X10Code", 2);
	fields->add<const char*, Variant>(&DeviceX10::getX10ModuleNameAsString, "X10Type", 3);
	fields->add<const URI&,URI>(&DeviceX10::getURI, "URI", 4, false);
	fields->add<const std::string&,Variant>(&DeviceX10::getEntityID, "EntityID", 5, false);
	fields->add<const Permission&,Permission>(&DeviceX10::getPermission, "Permission", 6, false);
	fields->add<const char*,Variant>(&DeviceX10::getClassName, "Type", 7);
	return fields;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool DeviceX10::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = Object::updateHash(hash_context);
	ret = hash_context->update( this->x10code.housecode ) & ret;
	ret = hash_context->update( this->x10code.unitcode ) & ret;
	ret = hash_context->update( this->x10codeStr ) & ret;
	ret = hash_context->update( this->type ) & ret;
	ret = hash_context->update( this->alias ) & ret;
	ret = hash_context->update( this->state ) & ret;
	ret = this->Interfaces.updateHash( hash_context ) & ret;
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

DeviceX10::MODULE_TYPE DeviceX10::getX10ModuleNameFromString( const std::string& ntype )
 {
	if ( ntype == "UNKNOWN" ) return DeviceX10::UNKNOWN;

	else if ( ntype == "AM12" )		return DeviceX10::AM12;
	else if ( ntype == "LM15" )		return DeviceX10::LM15;
	else if ( ntype == "LM12" )		return DeviceX10::LM12;
	else if ( ntype == "LM12W" )	return DeviceX10::LM12W;
	else if ( ntype == "LM12I" )	return DeviceX10::LM12I;
	else if ( ntype == "ML15ES" )	return DeviceX10::ML15ES;
	else if ( ntype == "LD11" )		return DeviceX10::LD11;
	else if ( ntype == "AWM2" )		return DeviceX10::AWM2;
	else if ( ntype == "MS13" )		return DeviceX10::MS13;
	else if ( ntype == "MS13E" )	return DeviceX10::MS13E;
	else if ( ntype == "AD10" )		return DeviceX10::AD10;
	else if ( ntype == "AW12" )		return DeviceX10::AW12;
	else if ( ntype == "SM10" )		return DeviceX10::SM10;
	else if ( ntype == "SM10E" )	return DeviceX10::SM10E;

	return DeviceX10::UNKNOWN;
 }

const char* DeviceX10::getX10ModuleNameAsString( DeviceX10::MODULE_TYPE mtype )
 {
	switch(mtype) {
		case DeviceX10::AM12:			return "AM12";
		case DeviceX10::LM12:			return "LM12";

		case DeviceX10::LM15:			return "LM15";

		case DeviceX10::LM12W:			return "LM12W";
		case DeviceX10::LM12I:			return "LM12I";

		case DeviceX10::ML15ES:			return "ML15ES";

		case DeviceX10::LD11:			return "LD11";

		case DeviceX10::AWM2:			return "AWM2";

		case DeviceX10::MS13:			return "MS13";
		case DeviceX10::MS13E:			return "MS13E";

		case DeviceX10::AD10:			return "AD10";
		case DeviceX10::AW12:			return "AW12";

		case DeviceX10::SM10:			return "SM10";
		case DeviceX10::SM10E:			return "SM10E";

		case DeviceX10::UNKNOWN:
		default:						return "UNKNOWN";
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

int x10codeStr2int( char* code )
 {
	return ((unsigned short)code[0] - 65) * 16 + atoi( code+1 ) -1;
 }

codeX10 string2codeX10( const std::string& code )
 {
	codeX10 res;
	res.housecode = 0;
	res.unitcode = 0;

	if ( code.size() == 0 )
		return res;
	if ( code.size() == 1 && code.at(0) == ' ' )
		return res;
	if ( code.find(',') != string::npos )
		return res;

	if ( code.size() > 0 )
		res.housecode = code.at(0) - 65;
	std::string unit = code.substr(1);

	res.unitcode = (unsigned short)atoi( unit.c_str() );
//qDebug() << code.c_str() << "|" << res.housecode << "." << res.unitcode << "|>" << code.at(0);
	return res;
 }

int string2codeX10num( const std::string& code )
 {
	codeX10 res = string2codeX10( code );
//qDebug() << code.c_str() << "|" << (int)res.housecode << res.unitcode << "|" << (int)res.housecode - 64;
	return ( (int)res.housecode )*16 + res.unitcode;
 }


std::string codeX102string( const codeX10& code )
 {
	std::string res = " ";
	res.at(0) = (char)((int)code.housecode+65);
	res += Utilities::toString<int>(code.unitcode);
//qDebug() << code.housecode << code.unitcode << "=>" << res.c_str();
	return res;
 }
 
